
package com.tools.cleanmaster.utils;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.os.StatFs;
import android.os.storage.StorageManager;
import android.text.format.Formatter;

import com.dianxinos.acceleratecore.logic.clean.intf.ICleanDirFileInfo;
import com.dianxinos.acceleratecore.logic.clean.intf.ICleanEnumDirFile;
import com.tools.acceleratecore.AccelerateCoreFactory;
import com.tools.acceleratecore.logic.clean.intf.ICleanMgr;
import com.tools.cleanmaster.appinfo.AppInfoCompat;
import com.tools.cleanmaster.appinfo.AppManagerCompat;
import com.tools.cleanmaster.controller.FileScanner;

import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class TrashUtils {
    private static final String TAG = "TrashUtils";

    /**
     * 默认层级，目前为4
     */
    public static final int DEPTH_DEFAULT = 4;

    /**
     * 最大的层数，目前为20
     */
    public static final int DEPTH_MAX = 20;

    //是否使用 jni优化过的函数
    private static boolean TRASH_USE_JNI = true;

    /**
     * 使用用户提供的深度来计算大小和文件数
     *
     * @param file
     * @param depth
     *
     * @return
     */
    public static long[] getFileCountAndSizeWithDepth(File file, int depth) {
        if (depth <= 0) {
            return new long[2];
        }
        if (TrashUtils.isUseJniFunction()) {
            return getFileCountAndSizeWithDepthJni(file, depth);
        } else {
            return getFileCountAndSize(file, 0, depth);
        }
    }

    private static long[] getFileCountAndSize(File file, int depth, int maxDepth) {
        if (file == null || !file.exists()) {
            return new long[2];
        }
        long fileCount = 0;
        long size = 0;
        if (file.isFile()) {
            fileCount = 1;
            size = file.length();
        } else {
            int currentDeep = depth + 1;
            if (currentDeep < maxDepth) {
                File[] subFiles = file.listFiles();
                if (subFiles != null) {
                    for (File subFile : subFiles) {
                        if (subFile.isFile()) {
                            fileCount += 1;
                            size += subFile.length();
                        } else {
                            long[] countAndSize = getFileCountAndSize(subFile, currentDeep,
                                    maxDepth);
                            fileCount += countAndSize[0];
                            size += countAndSize[1];
                        }
                    }
                }
            }
        }
        return new long[] {
                fileCount, size
        };
    }

    public static long getFileSizeDefautDepth(File file) {
        return getFileSize(file, 0, DEPTH_DEFAULT);
    }

    public static long getFileSizeWithDepth(File file, int depth) {
        if (depth <= 0) {
            return 0;
        }
        return getFileSize(file, 0, depth);
    }

    private static long getFileSize(File file, int depth, int maxDepth) {
        if (file == null || !file.exists()) {
            return 0;
        }
        long size = 0;
        if (file.isDirectory()) {
            int currentDeep = depth + 1;
            if (currentDeep < maxDepth) {
                File[] subFiles = file.listFiles();
                if (subFiles != null) {
                    for (int i = 0; i < subFiles.length; i++) {
                        if (subFiles[i].isDirectory()) {
                            size += getFileSize(subFiles[i], currentDeep, maxDepth);
                        } else if (subFiles[i].isFile()) {
                            size += subFiles[i].length();
                        }
                    }
                }
            }
        } else if (file.isFile()) {
            size = file.length();
        }
        return size;
    }

    @SuppressLint("NewApi")
    public static String[] findSDPaths(Context context) {
        String[] paths = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
            try {
                StorageManager sm = (StorageManager) context
                        .getSystemService(Context.STORAGE_SERVICE);
                Class<?>[] paramClasses = {};
                Method getVolumePaths = StorageManager.class.getMethod("getVolumePaths",
                        paramClasses);
                getVolumePaths.setAccessible(true);
                Object[] params = {};
                paths = (String[]) getVolumePaths.invoke(sm, params);
            } catch (NoSuchMethodException e1) {
                e1.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        if (paths == null
                && Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { // mounted
            File file = Environment.getExternalStorageDirectory();
            if (file.exists()) {
                String absolutePath = file.getAbsolutePath();
                paths = new String[] {
                        absolutePath
                };
            }
        }
        ArrayList<String> validPaths = new ArrayList<String>();
        if (paths != null && paths.length > 0) {
            for (String path : paths) {
                File file = new File(path);
                if (file.isDirectory()) {
                    String[] list = file.list();
                    if (list != null && list.length > 0) {
                        validPaths.add(path);
                    }
                }
            }
        }
        if (validPaths.isEmpty()) {
            return null;
        } else {
            return validPaths.toArray(new String[validPaths.size()]);
        }
    }

    /**
     * 获取file下的文件路径集合
     *
     * @param file
     * @param posFix
     *
     * @return
     */
    public static ArrayList<String> getFilePaths(File file, int depth, int maxDepth,
                                                 String[] posFix, boolean getLargeFile) {
        ArrayList<String> paths = new ArrayList<String>();
        if (file.isFile()) {
            String path = file.getAbsolutePath();
            if (getLargeFile && file.length() > FileScanner.LARGE_FILE_THRESHOLD) {
                paths.add(path);
            } else {
                if (posFix == null) {
                    paths.add(path);
                } else {
                    for (String pox : posFix) {
                        if (path.endsWith(pox)) {
                            paths.add(path);
                            break;
                        }
                    }
                }
            }
        } else {
            int currentDeep = depth + 1;
            if (currentDeep < maxDepth) {
                File[] subFils = null;
                try {
                    subFils = file.listFiles();// 这里有可能会产生OOM
                } catch (Throwable e) {
                }
                if (subFils != null && subFils.length > 0) {
                    for (File subFile : subFils) {
                        if (subFile.isFile()) {
                            String absolutePath = subFile.getAbsolutePath();
                            if (getLargeFile && file.length() > FileScanner.LARGE_FILE_THRESHOLD) {
                                paths.add(absolutePath);
                            } else {
                                if (posFix == null) {
                                    paths.add(absolutePath);
                                } else {
                                    for (String pox : posFix) {
                                        if (absolutePath.endsWith(pox)) {
                                            paths.add(absolutePath);
                                            break;
                                        }
                                    }
                                }
                            }
                        } else {
                            paths.addAll(getFilePaths(subFile, currentDeep, maxDepth, posFix,
                                    getLargeFile));
                        }
                    }
                }
            }
        }
        return paths;
    }

    public static boolean hasSdcard(String[] sdcards) {
        if (sdcards == null) {
            return false;
        }
        for (String rootDir : sdcards) {
            File file = new File(rootDir);
            File[] subFiles = file.listFiles();
            if (subFiles != null && subFiles.length > 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param context 获取所有sd卡信息
     */
    public static List<SdInfoItem> getAllSDInfo(Context context) {
        StorageList list = new StorageList(context);
        List<String> phonePaths = list.getMountedPhoneVolumePaths();
        List<String> sdPaths = list.getMountedSdCardVolumePaths();
        List<SdInfoItem> sizeItems = new ArrayList<SdInfoItem>();
        List<SdInfoItem> deleteItems = new ArrayList<SdInfoItem>();
        if (phonePaths != null && !phonePaths.isEmpty()) {
            for (String p : phonePaths) {
                SdInfoItem item = new SdInfoItem();
                item.type = 0;
                item.path = p;
                sizeItems.add(item);
            }
        }
        int n = 1;
        if (sdPaths != null && !sdPaths.isEmpty()) {
            for (int i = 0; i < sdPaths.size(); i++) {
                SdInfoItem item = new SdInfoItem();
                item.type = i + n;
                item.path = sdPaths.get(i);
                sizeItems.add(item);
            }
        }
        for (SdInfoItem item : sizeItems) {
            StatFs statFs = null;
            try {
                statFs = new StatFs(item.path);
            } catch (Exception e) {
                deleteItems.add(item);
                continue;
            }
            long totalsize = 0;
            long availablesize = 0;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                try {
                    Method getAvailableBytes = statFs.getClass().getMethod("getAvailableBytes");
                    Method getTotalBytes = statFs.getClass().getMethod("getTotalBytes");
                    availablesize = (Long) getAvailableBytes.invoke(statFs);
                    totalsize = (Long) getTotalBytes.invoke(statFs);
                } catch (Exception e) {
                    // 针对特定rom机型没有getAvailableBytes()该方法采取旧方法获取
                    long[] totalAndAvaliable = getTotalAndAvailableSize(statFs);
                    totalsize = totalAndAvaliable[0];
                    availablesize = totalAndAvaliable[1];
                }
            } else {
                long[] totalAndAvaliable = getTotalAndAvailableSize(statFs);
                totalsize = totalAndAvaliable[0];
                availablesize = totalAndAvaliable[1];
            }
            // 转化为可以显示的字符串
            try {

                long userSize = totalsize - availablesize;
                if (userSize < 0) {
                    userSize = 0;
                }
                item.totalsize = Formatter.formatFileSize(context, totalsize);
                item.useSize = Formatter.formatFileSize(context, userSize);
                item.progress = (int) (userSize * 100 / totalsize);
            } catch (Exception e) {
                deleteItems.add(item);
                continue;
            }

        }
        sizeItems.removeAll(deleteItems);
        return sizeItems;
    }

    private static long[] getTotalAndAvailableSize(StatFs statFs) {
        long[] totalAndAvaliable = new long[2];
        long blocksize = statFs.getBlockSize();
        long totalblocks = statFs.getBlockCount();
        long availableblocks = statFs.getAvailableBlocks();
        totalAndAvaliable[0] = blocksize * totalblocks;
        totalAndAvaliable[1] = availableblocks * blocksize;
        return totalAndAvaliable;
    }

    /**
     * 判断是否使用 清理相关的c层函数
     *
     * @return true调用c层函数，false调用java层函数
     */
    public static boolean isUseJniFunction() {
        return TRASH_USE_JNI && AccelerateCoreFactory.isLoadLib();
    }

    /**
     * list 目录下一级的所有的目录，对外提供
     *
     * @param file
     *
     * @return
     */
    public static String[] listDir(File file) {
        if (isUseJniFunction()) {
            return listDirJni(file);
        } else {
            return listDirJava(file);
        }
    }

    /**
     * list 目录下一级的所有的目录，目录使用c层遍历
     *
     * @param file 父目录，全路径
     *
     * @return 目录名称的数组
     */
    private static String[] listDirJni(File file) {
        if (file == null || !file.exists()) {
            return null;
        }

        ICleanMgr iCleanMgr = (ICleanMgr) AccelerateCoreFactory.getInstance().createInstance(ICleanMgr.class);
        ICleanEnumDirFile iCleanListResult =
                iCleanMgr.startScanDirEnumDirFileSyn(file.getAbsolutePath(), ICleanMgr.VALUE_INT_ENUM_DIR_BIT);
        if (null == iCleanListResult) {
            return null;
        }
        String[] allSubDirName = iCleanListResult.getDirArray();
        return allSubDirName;
    }

    /**
     * list 目录下一级的所有的目录，目录使用java层实现，作为so异常的后补方案
     *
     * @param file 父目录，全路径
     *
     * @return 目录名称的数组
     */
    private static String[] listDirJava(File file) {
        if (file == null || !file.exists()) {
            return null;
        }
        String[] dirArray = file.list(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String filename) {
                return new File(dir, filename).isDirectory();
            }
        });
        return dirArray;
    }

    /**
     * list 目录下一级的所有的目录 和 文件，目录使用c层遍历
     *
     * @param file 父目录文件对象
     *
     * @return 返回ICleanEnumDirFile接口对象，通过getDirArray返回目录名称数组；getFileArray返回文件名称数组。
     */
    public static ICleanEnumDirFile listDirAndFileJni(File file) {
        if (file == null || !file.exists()) {
            return null;
        }

        ICleanMgr iCleanMgr = (ICleanMgr) AccelerateCoreFactory.getInstance().createInstance(ICleanMgr.class);
        ICleanEnumDirFile iCleanListResult = iCleanMgr.startScanDirEnumDirFileSyn(file.getAbsolutePath(),
                ICleanMgr.VALUE_INT_ENUM_DIR_BIT | ICleanMgr.VALUE_INT_ENUM_FILE_BIT);
        return iCleanListResult;
    }

    /**
     * 计算 文件／文件夹的文件数和文件大小，目录使用c层递归遍历
     *
     * @param file  文件／目录对象
     * @param depth 限制层级
     *
     * @return 文件数，文件大小的数组
     */
    public static long[] getFileCountAndSizeWithDepthJni(File file, int depth) {
        long fileCount = 0;
        long size = 0;
        if (file == null || !file.exists()) {
            return new long[] {fileCount, size};
        }
        if (file.isFile()) {
            fileCount = 1;
            size = file.length();
        } else {
            //使用 c层 计算目录的 文件数，文件大小。
            ICleanMgr iCleanMgr = (ICleanMgr) AccelerateCoreFactory.getInstance().createInstance(ICleanMgr.class);
            ICleanDirFileInfo dirInfo = iCleanMgr.startScanDirFileInfoSyn(file.getAbsolutePath(), depth);
            if (null != dirInfo) {
                fileCount = dirInfo.getFileCount();
                size = dirInfo.getFileSize();
            }
        }
        if (FeatureConfig.DEBUG_LOG) {
            LogHelper.v(TAG, "jni getFileCountAndSizeWithDepthNew dir= "
                    + file.getAbsolutePath()
                    + ";depth=" + depth
                    + ";fileCount=" + fileCount
                    + ";size=" + size);
        }
        return new long[] {fileCount, size};
    }

    /**
     * 删除文件或目录，目录通过c层递归删除
     *
     * @param file          文件／目录对象
     * @param suffixExclude 需要过滤的文件后缀名，格式例如（.apk）（.apk＃.exe＃.txt）
     *
     * @return
     */
    public static boolean deleteFileJni(File file, String suffixExclude) {
        if (file == null || !file.exists()) {
            return false;
        }
        if (file.isFile()) {
            return file.delete();
        } else {
            ICleanMgr iCleanMgr = (ICleanMgr) AccelerateCoreFactory.getInstance().createInstance(ICleanMgr.class);
            boolean bRet = iCleanMgr.startDeleteDirSyn(file.getAbsolutePath(), suffixExclude);
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.i(TAG, "jni deleteFileNew dir= " + file.getAbsolutePath() + ";bRet=" + bRet);
            }
            return bRet;
        }
    }

    /**
     * 删除文件/目录。通过循环栈 替换 递归删除目录方式。
     *
     * @param rootFile
     * @param suffixExclude 不需要删除的后缀 e:.apk
     */
    public static void deleteFile(File rootFile, final String suffixExclude) {
        if (null == rootFile || null == suffixExclude) {
            return;
        }
        deleteFile(rootFile, new FilenameFilter() {
            @Override
            public boolean accept(File dir, String filename) {
                return !filename.endsWith(suffixExclude);
            }
        });
    }

    /**
     * 删除文件，不考虑后缀文件
     *
     * @param rootFile
     */
    public static void deleteFile(File rootFile) {
        deleteFile(rootFile, new FilenameFilter() {
            @Override
            public boolean accept(File dir, String filename) {
                return true;
            }
        });
    }

    /**
     * 删除文件/目录。通过循环栈 替换 递归删除目录方式。
     *
     * @param rootFile
     * @param filter   只删除 FilenameFilter.accept() 返回为true 文件
     */
    private static void deleteFile(File rootFile, FilenameFilter filter) {
        if (null == rootFile || null == filter) {
            return;
        }
        if (!rootFile.exists()) {
            return;
        }
        if (rootFile.isFile()) {
            if (filter.accept(rootFile.getParentFile(), rootFile.getName())) {
                rootFile.delete();
            }
            return;
        }
        List<File> dir_list = new ArrayList<File>();
        Stack<File> dir_stack = new Stack<File>();
        dir_stack.push(rootFile);

        while (!dir_stack.isEmpty()) {
            //取得栈顶元素，没有删除
            File file_item = dir_stack.peek();
            //遍历过的目录直接删除
            if (dir_list.contains(file_item)) {
                file_item.delete();
                dir_stack.pop();
                dir_list.remove(file_item);
                continue;
            }
            if (file_item.isFile()) {
                file_item.delete();
                dir_stack.pop();
            } else if (file_item.isDirectory()) {
                File[] files = file_item.listFiles();
                if (files != null && files.length != 0) {
                    for (int i = 0; i < files.length; i++) {
                        File children = files[i];
                        //只有目录才加入到循环栈。
                        if (children.isDirectory()) {
                            dir_stack.push(children);
                        } else {
                            if (filter.accept(file_item, children.getName())) {
                                children.delete();
                            }
                        }
                    }
                }
                //没有新元素入栈，直接删除掉。
                if (file_item == dir_stack.peek()) {
                    file_item.delete();
                    dir_stack.pop();
                } else {
                    //纪录 已经处理过的目录。
                    dir_list.add(file_item);
                }
            } else {
                file_item.delete();
                dir_stack.pop();
            }
        }
    }

    /**
     * 获取 路径转小写，不过sdcard根目录保持不变
     *
     * @param path       路径
     * @param sdCardDirs sdcard根目录 数组
     *
     * @return
     */
    public static String getSubDirLower(String path, String[] sdCardDirs) {
        if (null == path) {
            return null;
        }
        if (null == sdCardDirs) {
            return path.toLowerCase();
        }

        //目标路径 为 ""。
        if (path.isEmpty()) {
            return path;
        }
        for (String sdCardDirItem : sdCardDirs) {
            //根目录 ""放过。
            if (null == sdCardDirItem || sdCardDirItem.isEmpty()) {
                continue;
            }
            if (path.startsWith(sdCardDirItem)) {
                int start = sdCardDirItem.length();
                int end = path.length();
                //字符串等于根目录
                if (start == end) {
                    return path;
                }
                String subPath = path.substring(start, end);
                if (subPath.isEmpty()) {
                    return path;
                }
                String subPathLower = subPath.toLowerCase();
                if (subPathLower.equals(subPath)) {
                    return path;
                }
                String newPath = sdCardDirItem + subPathLower;
                return newPath;
            }
        }
        //没有匹配的
        return path.toLowerCase();
    }

    /**
     * 获取已安装包名列表
     */
    public static List<String> getInstallPkgs() {
        List<AppInfoCompat> appInfos = AppManagerCompat.getInstalledApplications();
        List<String> installedAppsPkgNames = new ArrayList<String>();
        for (AppInfoCompat appInfo : appInfos) {
            /*if (CleanConstant.PSAFE_FILTER && CleanConstant.PSAFE_PKGNAME.equals(appInfo.pkgName)) {
                continue;
            }*/
            installedAppsPkgNames.add(appInfo.pkgName);
        }
        return installedAppsPkgNames;
    }

    public static long getAllSDInfoUsedSize(Context context) {
        return 0;
    }
}
